#include "SSD1306_OLED.h"

void SSD1306_Base::init()
{
    send_cmd(0xAE); //关显示
    send_cmd(0xD5); //设置显示时钟分频率/振荡器频率(双字节指令)
    send_cmd(0x80);
    send_cmd(0xA8); //设置复用比(双字节指令)
    send_cmd(0x3F);
    send_cmd(0xD3); //设置显示偏离(双字节指针)
    send_cmd(0x00);
    send_cmd(0x40 | 0x00); //设置显示开始线
    send_cmd(0x8D);        //
    send_cmd(0x10 | 0x04); //设置高一点列开始寄存器………………
    send_cmd(0x20);        //设置内存地址模式(双字节)
    send_cmd(0x00);
    send_cmd(0xA0); //设置段复用
    send_cmd(0xC0); //设置com输出扫描方向
    send_cmd(0xDA); //设置COM引脚硬件配置(双字节)
    send_cmd(0x02 | 0x10);
    send_cmd(0x81); //设置对比度(双字节)
    send_cmd(0xFF);
    send_cmd(0xD9); //设置预充电周期(双字节)
    send_cmd(0x22);
    send_cmd(0xDB); //(双字节)
    send_cmd(0x30);
    send_cmd(0xA4); //关全屏显示
    send_cmd(0xA6); //设置正常显示
    send_cmd(0xA1);
    send_cmd(0xC8);
    send_cmd(0xAF); //开显示

    send_cmd(0x21);
    send_cmd(0x00);
    send_cmd(0xff);

    send_cmd(0x22);
    send_cmd(0x00);
    send_cmd(0x07);
}

int SSD1306_Base::begin()
{
    if (mode == SSD1306_SPI)
    {
        spi->begin();
    }
    else
    {
        i2c->begin();
        i2c->loadSlave(i2c_addr);
        if (!i2c->check())
        {
            i2c->deloadSlave();
            return 0;
        }
        i2c->deloadSlave();
    }

    if (mode == SSD1306_SPI)
    {
        gpio_init(dc, OUT_PP);
        gpio_init(cs, OUT_PP);
        gpio_write(cs, 1);

        if (rst != Pin_null)
        {
            gpio_init(rst, OUT_PP);
            gpio_write(rst, 1);
            gpio_write(rst, 0);
            delay_ms(50);
            gpio_write(rst, 1);
        }
    }

    init();
    clear();
    return 1;
}

uint8_t SSD1306_Base::send_cmd(uint8_t cmd)
{
    if (mode == SSD1306_I2C)
    {
        i2c->loadSlave(i2c_addr);
        i2c->write(0x00, cmd);
        i2c->deloadSlave();
    }
    else
    {
        gpio_write(dc, 0);
        spi->loadSlave(cs);
        spi->write(cmd);
        spi->deloadSlave();
    }
    return 1;
}

uint8_t SSD1306_Base::send_data(uint8_t data)
{
    if (mode == SSD1306_I2C)
    {
        i2c->loadSlave(i2c_addr);
        i2c->write(0x40, data);
        i2c->deloadSlave();
    }
    else
    {
        gpio_write(dc, 1);
        spi->loadSlave(cs);
        spi->write(data);
        spi->deloadSlave();
    }
    return 1;
}

uint8_t SSD1306_Base::send_data(uint8_t data, int count)
{
    if (mode == SSD1306_I2C)
    {
        i2c->loadSlave(i2c_addr);
        i2c->write(0x40, data, count);
        i2c->deloadSlave();
    }
    else
    {
        gpio_write(dc, 1);
        spi->loadSlave(cs);
        spi->write(data, count);
        spi->deloadSlave();
    }
    return 1;
}
uint8_t SSD1306_Base::send_data(const uint8_t *ptr, int size)
{
    if (mode == SSD1306_I2C)
    {
        i2c->loadSlave(i2c_addr);
        i2c->write(0x40, ptr, size);
        i2c->deloadSlave();
    }
    else
    {
        gpio_write(dc, 1);
        spi->loadSlave(cs);
        spi->write(ptr, size);
        spi->deloadSlave();
    }
    return 1;
}

int SSD1306_Base::chack()
{
    if (mode == SSD1306_I2C)
    {
        i2c->loadSlave(i2c_addr);
        return i2c->check();
    }
    return 1;
}

void SSD1306_Base::setLight(uint8_t power)
{
    send_cmd(0x81);
    send_cmd(power);
}

void SSD1306_Base::setShowMode(uint8_t mode)
{
    if (mode == 0)
    {
        send_cmd(0xA7);
    }
    else
    {
        send_cmd(0xA6);
    }
}

/********************************************************/
void SSD1306_GUI::clear()
{
    fillScreen(0);

    send_cmd(0x21);
    send_cmd(0x00);
    send_cmd(0xff);

    send_data((uint8_t)0x00, 1024);
}

void SSD1306_GUI::show()
{
    send_data(buffer, 1024);
}

void SSD1306_GUI::drawPixel(int16_t x, int16_t y, uint16_t color)
{
    if ((x >= 0) && (x < width()) && (y >= 0) && (y < height()))
    {
        // Pixel is in-bounds. Rotate coordinates if needed.
        int16_t t;
        switch (rotation)
        {
        case 1:
            t = x;
            x = WIDTH - 1 - y;
            y = t;
            break;
        case 2:
            x = WIDTH - 1 - x;
            y = HEIGHT - 1 - y;
            break;
        case 3:
            t = x;
            x = y;
            y = HEIGHT - 1 - t;
            break;
        }
        switch (color)
        {
        case SSD1306_WHITE:
            buffer[x + (y / 8) * WIDTH] |= (1 << (y & 7));
            break;
        case SSD1306_BLACK:
            buffer[x + (y / 8) * WIDTH] &= ~(1 << (y & 7));
            break;
        case SSD1306_INVERSE:
            buffer[x + (y / 8) * WIDTH] ^= (1 << (y & 7));
            break;
        }
    }
}

/***************************************************************/

size_t SSD1306_Console::write(uint8_t assci)
{
    int write_width, write_height, write_mode_size;
    write_width = text_font->font_width + 1;
    write_height = text_font->font_height;
    write_mode_size = text_font->mode_size;
    if (assci == '\n')
    {
        write_x = 0;
        write_line += write_height / 8;
        return 1;
    }
    if (assci < ' ' || assci > '~')
    {
        assci = 32;
    }
    assci -= 32;

    const uint8_t *assci_ptr = text_font->font_ptr + assci * write_mode_size;

    if (write_x + write_width >= 128)
    {
        write_x = 0;
        write_line++;
    }

    if (write_line > 7)
    {
        clear();
        write_line = 0;
    }

    send_cmd(0x21);
    send_cmd((uint8_t)write_x);
    send_cmd((uint8_t)(write_x + write_width - 1));
    send_cmd(0x22);
    send_cmd((uint8_t)write_line);
    send_cmd((uint8_t)(write_line + write_height / 2 - 1));

    send_data(assci_ptr, write_mode_size);

    write_x += write_width;

    return 0;
}

size_t SSD1306_Console::write(const uint8_t *buffer, size_t size)
{
    for (int i = 0; i < size; i++)
    {
        write(buffer[i]);
    }
    return 0;
}

void SSD1306_Console::clear()
{
    send_cmd(0x21);
    send_cmd(0x00);
    send_cmd(0xff);
    send_cmd(0x22);
    send_cmd(0x00);
    send_cmd(0x07);

    send_data((uint8_t)0x00, 1024);
}
